package com.inifitness.tenant.core.system.service.impl;


import java.awt.Font;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import jakarta.servlet.http.HttpServletRequest;

import com.inifitness.common.constants.GlobalConstants;
import com.inifitness.common.constants.SecurityConstants;
import com.inifitness.common.enums.CaptchaTypeEnum;
import com.inifitness.tenant.core.config.captcha.CaptchaProperties;
import com.inifitness.tenant.core.security.util.JwtUtils;
import com.inifitness.tenant.core.system.model.dto.CaptchaResult;
import com.inifitness.tenant.core.system.model.dto.LoginResult;
import com.inifitness.tenant.core.system.service.BizAuthService;

import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.jwt.RegisteredPayload;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * 认证服务实现类
 *
 * @author sunjinfeng
 * @since 2024/04/18 11:00:00
 */
@Service
@RequiredArgsConstructor
@Log4j2
public class BizAuthServiceImpl implements BizAuthService {

  private final AuthenticationManager authenticationManager;
  private final RedisTemplate<String, Object> redisTemplate;
  private final CodeGenerator codeGenerator;
  private final Font captchaFont;
  private final CaptchaProperties captchaProperties;

  /**
   * 登录
   *
   * @param username 用户名
   * @param password 密码
   * @return 登录结果
   */
  @Override
  public LoginResult login(String username, String password) {
    UsernamePasswordAuthenticationToken authenticationToken =
        new UsernamePasswordAuthenticationToken(username.toLowerCase(Locale.CHINA).trim(), password);
    Authentication authentication = authenticationManager.authenticate(authenticationToken);
    String accessToken = JwtUtils.generateToken(authentication);
    return LoginResult.builder()
        .tokenType(GlobalConstants.BEARER_TOKEN_TYPE)
        .accessToken(accessToken)
        .build();
  }

  /**
   * 注销
   */
  @Override
  public void logout() {
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    if (Objects.isNull(attributes)){
      return;
    }
    HttpServletRequest request = attributes.getRequest();
    String token = request.getHeader(HttpHeaders.AUTHORIZATION);
    if (CharSequenceUtil.isNotBlank(token)) {

      Map<String, Object> claims = JwtUtils.parseToken(token);
      String jti = Convert.toStr(claims.get(RegisteredPayload.JWT_ID));
      Long expiration = Convert.toLong(claims.get(RegisteredPayload.EXPIRES_AT));
      if (expiration != null) {
        long ttl = expiration - System.currentTimeMillis() / GlobalConstants.VALUE_1000;
        redisTemplate.opsForValue()
            .set(SecurityConstants.BLACKLIST_TOKEN_PREFIX + jti, null, ttl, TimeUnit.SECONDS);
      } else {
        redisTemplate.opsForValue().set(SecurityConstants.BLACKLIST_TOKEN_PREFIX + jti, null);
      }
    }
    SecurityContextHolder.clearContext();
  }

  /**
   * 获取验证码
   *
   * @return 验证码
   */
  @Override
  public CaptchaResult getCaptcha() {

    String captchaType = captchaProperties.getType();
    AbstractCaptcha captcha = generateCaptcha(captchaType);
    captcha.setGenerator(codeGenerator);
    captcha.setTextAlpha(captchaProperties.getTextAlpha());
    captcha.setFont(captchaFont);

    String captchaCode = captcha.getCode();
    String imageBase64Data = captcha.getImageBase64Data();

    // 验证码文本缓存至Redis，用于登录校验
    String captchaKey = IdUtil.fastSimpleUUID();
    redisTemplate.opsForValue().set(SecurityConstants.CAPTCHA_CODE_PREFIX + captchaKey, captchaCode,
        captchaProperties.getExpireSeconds(), TimeUnit.SECONDS);

    return CaptchaResult.builder()
        .captchaKey(captchaKey)
        .captchaBase64(imageBase64Data)
        .build();
  }

  private AbstractCaptcha generateCaptcha(String captchaType) {
    int width = captchaProperties.getWidth();
    int height = captchaProperties.getHeight();
    int interfereCount = captchaProperties.getInterfereCount();
    int codeLength = captchaProperties.getCode().getLength();

    AbstractCaptcha captcha;
    if (CaptchaTypeEnum.CIRCLE.name().equalsIgnoreCase(captchaType)) {
      captcha = CaptchaUtil.createCircleCaptcha(width, height, codeLength, interfereCount);
    } else if (CaptchaTypeEnum.GIF.name().equalsIgnoreCase(captchaType)) {
      captcha = CaptchaUtil.createGifCaptcha(width, height, codeLength);
    } else if (CaptchaTypeEnum.LINE.name().equalsIgnoreCase(captchaType)) {
      captcha = CaptchaUtil.createLineCaptcha(width, height, codeLength, interfereCount);
    } else if (CaptchaTypeEnum.SHEAR.name().equalsIgnoreCase(captchaType)) {
      captcha = CaptchaUtil.createShearCaptcha(width, height, codeLength, interfereCount);
    } else {
      throw new IllegalArgumentException("Invalid captcha type: " + captchaType);
    }
    return captcha;
  }

}
